# -*- coding: utf-8 -*-#

# -------------------------------------------------------------------------------
# Name:         dataset.Label_Maker
# Description:
# Author:       yhm
# Date:         2020/9/29
# -------------------------------------------------------------------------------
from shapely.geometry import Polygon
import numpy as np
from db_config import cfg
import cv2
import pyclipper


def _compute_distance(xs, ys, point_1, point_2):
    square_distance_1 = np.square(xs - point_1[0]) + np.square(ys - point_1[1])
    square_distance_2 = np.square(xs - point_2[0]) + np.square(ys - point_2[1])
    square_distance = np.square(point_1[0] - point_2[0]) + np.square(point_1[1] - point_2[1])
    square_distance_1[square_distance_1 <= 0] = 0.001
    square_distance_2[square_distance_2 <= 0] = 0.001
    cosin = (square_distance - square_distance_1 - square_distance_2) / \
            (2 * np.sqrt(square_distance_1 * square_distance_2))
    square_sin = 1 - np.square(cosin)
    square_sin = np.nan_to_num(square_sin)
    square_sin[square_sin <= 0] = 0.001
    square_distance = 0.001 if square_distance <= 0 else square_distance
    result = np.sqrt(square_distance_1 * square_distance_2 * square_sin / square_distance)
    result[cosin < 0] = np.sqrt(np.fmin(square_distance_1, square_distance_2))[cosin < 0]
    return result


def make_score_map(anns, gt, mask):
    min_text_size = cfg.TRAIN.MIN_TEXT_SIZE
    shrink_ratio = cfg.SHRINK_RATIO

    for ann in anns:
        poly = np.array(ann['poly'])
        height = max(poly[:, 1]) - min(poly[:, 1])
        width = max(poly[:, 0]) - min(poly[:, 0])
        polygon = Polygon(poly)
        if polygon.area < 1 or min(height, width) < min_text_size or ann['text'] == '###':
            cv2.fillPoly(mask, poly.astype(np.int32)[np.newaxis, :, :], 0)
            continue
        else:
            distance = polygon.area * (1 - np.power(shrink_ratio, 2)) / polygon.length
            subject = [tuple(l) for l in ann['poly']]
            padding = pyclipper.PyclipperOffset()
            padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
            shrinked = padding.Execute(-distance)
            if len(shrinked) == 0:
                cv2.fillPoly(mask, poly.astype(np.int32)[np.newaxis, :, :], 0)
                continue
            else:
                shrinked = np.array(shrinked[0]).reshape(-1, 2)
                if shrinked.shape[0] > 2 and Polygon(shrinked).is_valid:
                    cv2.fillPoly(gt, [shrinked.astype(np.int32)], 1)
                else:
                    cv2.fillPoly(mask, poly.astype(np.int32)[np.newaxis, :, :], 0)
                    continue
    return gt, mask


def make_border_map(ann, canvas, mask):
    polygon = np.array(ann['poly'])
    assert polygon.ndim == 2
    assert polygon.shape[1] == 2

    polygon_shape = Polygon(polygon)
    if polygon_shape.area <= 0:
        return
    # 多边形矩阵扩大面积
    distance = polygon_shape.area * (1 - np.power(cfg.SHRINK_RATIO, 2)) / polygon_shape.length
    subject = [tuple(l) for l in polygon]
    padding = pyclipper.PyclipperOffset()
    padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
    padded_polygon = np.array(padding.Execute(distance)[0])

    cv2.fillPoly(mask, [padded_polygon.astype(np.int32)], 1.0)

    xmin = padded_polygon[:, 0].min()
    xmax = padded_polygon[:, 0].max()
    ymin = padded_polygon[:, 1].min()
    ymax = padded_polygon[:, 1].max()
    width = xmax - xmin + 1
    height = ymax - ymin + 1

    polygon[:, 0] = polygon[:, 0] - xmin
    polygon[:, 1] = polygon[:, 1] - ymin

    xs = np.broadcast_to(np.linspace(0, width - 1, num=width).reshape(1, width), (height, width))
    ys = np.broadcast_to(np.linspace(0, height - 1, num=height).reshape(height, 1), (height, width))

    distance_map = np.zeros((polygon.shape[0], height, width), dtype=np.float32)
    for i in range(polygon.shape[0]):
        j = (i + 1) % polygon.shape[0]
        absolute_distance = _compute_distance(xs, ys, polygon[i], polygon[j])
        distance_map[i] = np.clip(absolute_distance / distance, 0, 1)
    distance_map = np.min(distance_map, axis=0)

    xmin_valid = min(max(0, xmin), canvas.shape[1] - 1)
    xmax_valid = min(max(0, xmax), canvas.shape[1] - 1)
    ymin_valid = min(max(0, ymin), canvas.shape[0] - 1)
    ymax_valid = min(max(0, ymax), canvas.shape[0] - 1)
    # print(canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1].shape)
    # print((1 - distance_map[
    #            ymin_valid - ymin:ymax_valid - ymin + 1,
    #            xmin_valid - xmin:xmax_valid - xmin + 1]).shape)
    # print(">>>>>>>>>>>>>>")
    canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1] = np.fmax(
        1 - distance_map[
            ymin_valid - ymin:ymax_valid - ymin + 1,
            xmin_valid - xmin:xmax_valid - xmin + 1],
        canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1])
    return canvas, mask
